﻿using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Net;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Wulong.Phone.Utils.Rx;

namespace Wulong.Phone.Utils
{
    public static class ObservableResource
    {
        private const string CacheFolder = "cache"; 
        public static void CacheResource(MemoryStream toCache, string cacheKey)
        {
            string targetFile = Path.Combine(CacheFolder, cacheKey); 
            using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!isoFile.DirectoryExists(CacheFolder))                
                    isoFile.CreateDirectory(CacheFolder); 
                if (isoFile.FileExists(targetFile))                
                    isoFile.DeleteFile(targetFile); 
                using (IsolatedStorageFileStream outputStream = isoFile.CreateFile(targetFile))
                {
                    toCache.WriteTo(outputStream);
                }
            }
        } 
        public static IObservable<MemoryStream> FromCache(string cacheKey)
        {
            return new AnonymousObservable<MemoryStream>(observer =>
                                                             {
                                                                 string targetFile = Path.Combine(CacheFolder, cacheKey); 
                                                                 using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
                                                                 {
                                                                     if (!isoFile.FileExists(targetFile))
                                                                     {
                                                                         observer.OnNext(null);
                                                                     } 
                                                                     else
                                                                     {
                                                                         using (IsolatedStorageFileStream inputStream = isoFile.OpenFile(targetFile, FileMode.Open, FileAccess.Read))
                                                                         {
                                                                             observer.OnNext(inputStream.ToMemoryStream());
                                                                         }
                                                                     }
                                                                 } 
                                                                 observer.OnCompleted(); 
                                                                 return Disposable.Empty;
                                                             });
        } 
        
        public static IObservable<MemoryStream> FromInternet(Uri onlineResource)
        {
            return new AnonymousObservable<WebResponse>(observer =>
                                                            {
                                                                var httpWebRequest = (HttpWebRequest)WebRequest.Create(onlineResource); 
                                                                httpWebRequest.BeginGetResponse(iar =>
                                                                                                    {
                                                                                                        WebResponse response; 
                                                                                                        try
                                                                                                        {
                                                                                                            var requestState = (HttpWebRequest)iar.AsyncState; 
                                                                                                            response = requestState.EndGetResponse(iar);
                                                                                                        } 
                                                                                                        catch (Exception exception)
                                                                                                        {
                                                                                                            observer.OnError(exception); return;
                                                                                                        } 
                                                                                                        observer.OnNext(response); 
                                                                                                        observer.OnCompleted();
                                                                                                    }, httpWebRequest); 
                                                                return Disposable.Empty;
                                                            })
                                                            .Select(response => response.GetResponseStream().ToMemoryStream());
        }

        public static IObservable<MemoryStream> CacheAsFallback(Uri onlineResource, string cacheKey)
        {
            IObservable<MemoryStream> observableOnlineResource = FromInternet(onlineResource); 
            return observableOnlineResource.Select(stream =>
                                                       {
                                                           CacheResource(stream, cacheKey); 
                                                           stream.Position = 0; return stream;
                                                       })
                                            .Catch<MemoryStream, Exception>(exception => 
                                                FromCache(cacheKey).Select(cacheStream =>
                                                                               {
                                                                                   if (cacheStream == null)                                                    
                                                                                       throw new Exception("No cache"); 
                                                                                   return cacheStream;
                                                                               }));
        } 

    }
}
